package com.mintyfresh.tns.util;
import java.lang.Math;
import java.util.*;
import java.io.Serializable;

//FIXME
//This Class desperately needs bounds checking. All methods that should ensure their parameters are within bounds have been specified, but not exceptions are actually thrown.
public class Terrain implements Serializable {
	private ArrayList<Pair> pairs;
	/* 
	 * Be extremely careful using ypairs.
	 * They rely on the assumption that only one y exists for each x.
	 */
	private static int radius = 40;
	private static final double percentStep = 0.004;
	private static final int startHeight = 400; 
	private static final int xMax = 800;
	private static final int yMax = 600;
	/**
	 *	Constructor
	 */
	public Terrain() {
		pairs = new ArrayList<Pair>();
	}

	/**
	 *	Add a pair of ints to the Terrain 
	 *	
	 *	@param x 	Integer value between 0 and 800
	 *	@param y 	Integer value between 0 and 600
	 *	@throws IndexOutOfBoundsException if x or y is not within bounds
	 */
	public void add(int x, int y) {
		pairs.add(new Pair(x, y));
	}

	/**
	 *	Add a Pair object to the Terrain
	 *
	 *	@param p 	Pair object with x between 0 and 800 and y between 0 and 600
	 *	@throws IndexOutOfBoundsException if x or y is not within bounds
	 */
	public void add(Pair p) {
		pairs.add(p);
	}

	/**
	 *	Helper function that drops the y point at an x point by a given value
	 *
	 *	@param x		Integer value between 0 and 800
	 *	@param drop 	Integer value such that y(x)+drop < 600
	 */
	private void drop(int x, int drop) {
		if(x < 0) x = 0;
		if(x > 800) x = 800;
		Pair pair = pairs.get(x);
		double oldX = pair.x;
		double oldY = pair.y;
		pair.y += drop;
		pairs.set(x, pair);
		double newX = pair.x;
		double newY = pair.y;
	}

	/**
	 *	Create the terrain object by randomly choosing a y value for each x value
	 *
	 *	@param percentStep	Percent of the height to go up (or down).
	 *				     Lower value means smoother but more even terrain
	 *	@param startHeight	Height to begin choosing values at, between 0 and 800
	 *	@throws IndexOutOfBoundsException if startHeight is not between 0 and 600
	 */
	public void generate(Random generator) {
		int y = startHeight;
		for(int x = 0; x <= xMax; x++) {
			int goUp = generator.nextInt(2);
			if(goUp == 1) {
				y = (int)Math.ceil((1 + percentStep)*y);
			} else {
				y = (int)Math.floor((1 - percentStep)*y);
			}
			if(y < 0) y = 1;
			if(y > yMax) y = yMax;
			add(new Pair(x, y));
		}
	}

	/**
	 *	Destroy the terrain around an x point
	 *
	 *	@param x	Integer value between 0 and 800.
	 *	@throws IndexOutOfBoundsException if x is less than 0 or greater than 800
	 */
	public void destroyTerrain(int x) {
		int drop = (int)Math.round(radius);
		drop(x, drop--);
		for(int i = 1; i < radius; i++) {
			drop(x+i, drop--);
		}
		drop = (int)Math.round(radius);
		for(int i = 1; i < radius; i++) {
			drop((x-i), drop--);
		}
	}

	/**
	 *	Test whether a point is below the ground
	 *
	 *	@param x	Integer value between 0 and 800
	 *	@param y	Integer value between 0 and 600
	 *	@throws IndexOutOfBoundsException if x is less than 0 or greater than 800
	 */
	public boolean isBelowGround(int x, int y) {
		if(x < 0) x = 0;
		if(x > 800) x = 800;
		Pair p = pairs.get(x);
		double ground = p.y;
		if(y >= ground) return true;
		return false;
	}

	/**
	 *	Access the Pairs arraylist (most likely for iteration)
	 */
	public ArrayList<Pair> getPairs() {
		return pairs;
	}

	/**
	 *	Get the y coordinate corrosponding to this x
	 */
	public Pair get(int x) {
		return pairs.get(x);
	}

	/**
	 * 	Prints out a simple view of the terrain for debugging
	public String toString() {
		String s = new String();
		Collection<Pair> pairs = this.pairs;
		for(Pair p : pairs) {
			s += ("(" + p.x + ", " + p.x + ")\n");
		}
		return s;
	}
	*/
}
